#version 400



// PN patch data
struct PnPatch
{
 float b210;
 float b120;
 float b021;
 float b012;
 float b102;
 float b201;
 float b111;
 float n110;
 float n011;
 float n101;
};

layout(vertices = 3) out;

uniform float lodLevel1;
uniform float lodLevel2;
uniform float lodLevel3;

uniform vec3 lodPos;

uniform bool cameraView;

in vec3 vPosition[];
in vec3 vNormal[];
in vec3 vLightDir[];

out vec3 tcPosition[];
out vec3 tcLightDir[];

out vec3 tcNormal[3];
out PnPatch oPnPatch[3];

out vec3 tcWireframeColor[3];

//float tessLevelInner = 20.0; 
//float tessLevelOuter = 20.0; 


float wij(int i, int j)
{
 return dot(vPosition[j].xyz - vPosition[i].xyz, vNormal[i]);
}

float vij(int i, int j)
{
 vec3 Pj_minus_Pi = vPosition[j].xyz
                  - vPosition[i].xyz;
 vec3 Ni_plus_Nj  = vNormal[i]+vNormal[j];
 return 2.0*dot(Pj_minus_Pi, Ni_plus_Nj)/dot(Pj_minus_Pi, Pj_minus_Pi);
}

float GetTessLevel(float Distance0, float Distance1)                                            
{                                                                                               
    float AvgDistance = (Distance0 + Distance1) / 2.0;                                          
                                                                                                
    if (AvgDistance <= 1.6) 
	{
		tcWireframeColor[gl_InvocationID] = vec3(0.0, 0.0, 1.0);          
        return lodLevel1;                                                                            
    }                                                                                           
    else if (AvgDistance <= 2.0) 
	{
		tcWireframeColor[gl_InvocationID] = vec3(0.0, 1.0, 0.0);                                                              
        return lodLevel2;                                                                           
    }                                                                                           
    else 
	{
		tcWireframeColor[gl_InvocationID] = vec3(1.0, 1.0, 1.0);                                                                                     
        return lodLevel3;
    }
}             

void main()
{
	
	tcPosition[gl_InvocationID] = vPosition[gl_InvocationID];
	tcNormal[gl_InvocationID] = vNormal[gl_InvocationID];
	tcLightDir[gl_InvocationID] = vLightDir[gl_InvocationID];
	
	// set base
	 float P0 = vPosition[0][gl_InvocationID];
	 float P1 = vPosition[1][gl_InvocationID];
	 float P2 = vPosition[2][gl_InvocationID];
	 float N0 = vNormal[0][gl_InvocationID];
	 float N1 = vNormal[1][gl_InvocationID];
	 float N2 = vNormal[2][gl_InvocationID];

	 // compute control points
	 oPnPatch[gl_InvocationID].b210 = (2.0*P0 + P1 - wij(0,1)*N0)/3.0;
	 oPnPatch[gl_InvocationID].b120 = (2.0*P1 + P0 - wij(1,0)*N1)/3.0;
	 oPnPatch[gl_InvocationID].b021 = (2.0*P1 + P2 - wij(1,2)*N1)/3.0;
	 oPnPatch[gl_InvocationID].b012 = (2.0*P2 + P1 - wij(2,1)*N2)/3.0;
	 oPnPatch[gl_InvocationID].b102 = (2.0*P2 + P0 - wij(2,0)*N2)/3.0;
	 oPnPatch[gl_InvocationID].b201 = (2.0*P0 + P2 - wij(0,2)*N0)/3.0;
	 float E = ( oPnPatch[gl_InvocationID].b210
			   + oPnPatch[gl_InvocationID].b120
			   + oPnPatch[gl_InvocationID].b021
			   + oPnPatch[gl_InvocationID].b012
			   + oPnPatch[gl_InvocationID].b102
			   + oPnPatch[gl_InvocationID].b201 ) / 6.0;
	 float V = (P0 + P1 + P2)/3.0;
	 oPnPatch[gl_InvocationID].b111 = E + (E - V)*0.5;
	 oPnPatch[gl_InvocationID].n110 = N0+N1-vij(0,1)*(P1-P0);
	 oPnPatch[gl_InvocationID].n011 = N1+N2-vij(1,2)*(P2-P1);
	 oPnPatch[gl_InvocationID].n101 = N2+N0-vij(2,0)*(P0-P2);

	vec3 eyeWorldPos = vec3(0.0, 0.0, 1.0);

	//================ LOD computations ========================
	float eyeToVertDist0 = distance(lodPos, tcPosition[0] );
	float eyeToVertDist1 = distance(lodPos, tcPosition[1] );
	float eyeToVertDist2 = distance(lodPos, tcPosition[2] );
	
	//===========================================================

	/*
	//Distance
	gl_TessLevelOuter[0] = GetTessLevel(eyeToVertDist0, eyeToVertDist2); //tessLevelOuter; // times to subdivide second side
	gl_TessLevelOuter[1] = GetTessLevel(eyeToVertDist0, eyeToVertDist1); //tessLevelOuter; // times to subdivide third side
	gl_TessLevelOuter[2] = GetTessLevel(eyeToVertDist1, eyeToVertDist2); //tessLevelOuter; // times to subdivide first side
						   
	gl_TessLevelInner[0] = gl_TessLevelOuter[2];  //tessLevelInner; // number of nested primitives to generate
	*/

	//Reflection
	/*vec3 vPos = normalize(vPosition[0] + vPosition[1] + vPosition[2]);
	vec3 view = lodPos - vPos;
	vec3 N = normalize(vNormal[0] + vNormal[1] + vNormal[2]);
	float tesslevel = max(dot(view,N), 0.0f) *20.0f +1.0f;
	gl_TessLevelOuter[0] = tesslevel;
	gl_TessLevelOuter[1] = tesslevel;
	gl_TessLevelOuter[2] = tesslevel;*/

	//Distance and culling
	vec3 view;
	if(!cameraView)
	{
		vec3 vPos = (vPosition[0] + vPosition[1] + vPosition[2])/3.0;
		view = normalize(lodPos - vPos);
	}
	else
	{
		view = normalize(vLightDir[gl_InvocationID]);// + vLightDir[1] + vLightDir[2]);
	}
	
	vec3 N = normalize(vNormal[0] + vNormal[1] + vNormal[2]);
	//float tesslevel = max(dot(view,N), 0.0f) *20.0f +1.0f;
	if(!cameraView)
	{
		if(dot(view,N) > -0.5f)
		{
			gl_TessLevelOuter[0] = GetTessLevel(eyeToVertDist0, eyeToVertDist2); //tessLevelOuter; // times to subdivide second side
			gl_TessLevelOuter[1] = GetTessLevel(eyeToVertDist0, eyeToVertDist1); //tessLevelOuter; // times to subdivide third side
			gl_TessLevelOuter[2] = GetTessLevel(eyeToVertDist1, eyeToVertDist2); //tessLevelOuter; // times to subdivide first side
							   
			gl_TessLevelInner[0] = gl_TessLevelOuter[2];
		}
		else
		{
			gl_TessLevelOuter[0] = 0.0f;
			gl_TessLevelOuter[1] = 0.0f;
			gl_TessLevelOuter[2] = 0.0f;

			gl_TessLevelInner[0] = 0.0f;
		}
	}
	else
	{
		
			gl_TessLevelOuter[0] = GetTessLevel(eyeToVertDist0, eyeToVertDist2); //tessLevelOuter; // times to subdivide second side
			gl_TessLevelOuter[1] = GetTessLevel(eyeToVertDist0, eyeToVertDist1); //tessLevelOuter; // times to subdivide third side
			gl_TessLevelOuter[2] = GetTessLevel(eyeToVertDist1, eyeToVertDist2); //tessLevelOuter; // times to subdivide first side
							   
			gl_TessLevelInner[0] = gl_TessLevelOuter[2];
	}

	/*
	tcPosition[gl_InvocationID] = vPosition[gl_InvocationID];

	gl_TessLevelInner[0] = tessLevelInner; // number of nested primitives to generate
	gl_TessLevelOuter[0] = tessLevelOuter; // times to subdivide first side
	gl_TessLevelOuter[1] = tessLevelOuter; // times to subdivide second side
	gl_TessLevelOuter[2] = tessLevelOuter; // times to subdivide third side
	*/
}